// 一二三阶
const _ = require("lodash");
const {
  readAllValues,
} = require("../awakened-potential/helpers/readAllValues.js");

/**
 * 热号 + 冷号预测
 */
function predictNextNumber(history, options = {}) {
  const hotCount = options.hotCount || 21;
  const coldCount = options.coldCount || 11;

  const countMap = Array(50).fill(0);

  for (const item of history) {
    const number = Number(item.number);
    if (number >= 1 && number <= 49) {
      countMap[number]++;
    }
  }

  const result = countMap.map((count, number) => ({ number, count })).slice(1);

  const hotNumbers = _.orderBy(result, ["count", "number"], ["desc", "asc"])
    .slice(0, hotCount)
    .map((item) => item.number);

  const coldNumbers = _.orderBy(result, ["count", "number"], ["asc", "asc"])
    .slice(0, coldCount)
    .map((item) => item.number);

  const recommend = _.uniq([...hotNumbers, ...coldNumbers]);

  return {
    hot: hotNumbers,
    cold: coldNumbers,
    recommend: recommend.sort((a, b) => a - b),
  };
}

/**
 * 一阶马尔科夫
 */
function buildMarkovMatrix(history) {
  const matrix = Array.from({ length: 50 }, () => Array(50).fill(0));

  for (let i = 1; i < history.length; i++) {
    const prev = Number(history[i - 1].number);
    const curr = Number(history[i].number);
    if (prev >= 1 && curr >= 1 && prev <= 49 && curr <= 49) {
      matrix[prev][curr]++;
    }
  }

  for (let i = 1; i <= 49; i++) {
    const row = matrix[i];
    const sum = row.reduce((a, b) => a + b, 0);
    if (sum > 0) {
      for (let j = 1; j <= 49; j++) {
        row[j] = row[j] / sum;
      }
    }
  }

  return matrix;
}

function getMarkovPrediction(prev, matrix, topN = 5) {
  const row = matrix[prev];
  if (!row) return [];

  return row
    .map((prob, number) => ({ number, prob }))
    .slice(1)
    .filter((item) => item.prob > 0)
    .sort((a, b) => b.prob - a.prob)
    .slice(0, topN)
    .map((item) => item.number);
}

/**
 * 二阶马尔科夫
 */
function buildSecondOrderMarkovMatrix(history) {
  const matrix = Array.from({ length: 50 }, () =>
    Array.from({ length: 50 }, () => Array(50).fill(0))
  );

  for (let i = 2; i < history.length; i++) {
    const p1 = Number(history[i - 2].number);
    const p2 = Number(history[i - 1].number);
    const curr = Number(history[i].number);

    if ([p1, p2, curr].every((n) => n >= 1 && n <= 49)) {
      matrix[p1][p2][curr]++;
    }
  }

  for (let i = 1; i <= 49; i++) {
    for (let j = 1; j <= 49; j++) {
      const row = matrix[i][j];
      const sum = row.reduce((a, b) => a + b, 0);
      if (sum > 0) {
        for (let k = 1; k <= 49; k++) {
          row[k] = row[k] / sum;
        }
      }
    }
  }

  return matrix;
}

function getSecondOrderMarkovPrediction(p1, p2, matrix, topN = 5) {
  const row = matrix[p1]?.[p2];
  if (!row) return [];

  return row
    .map((prob, number) => ({ number, prob }))
    .slice(1)
    .filter((item) => item.prob > 0)
    .sort((a, b) => b.prob - a.prob)
    .slice(0, topN)
    .map((item) => item.number);
}

/**
 * 三阶马尔科夫
 */
function buildThirdOrderMarkovMatrix(history) {
  const matrix = Array.from({ length: 50 }, () =>
    Array.from({ length: 50 }, () =>
      Array.from({ length: 50 }, () => Array(50).fill(0))
    )
  );

  for (let i = 3; i < history.length; i++) {
    const [p1, p2, p3, curr] = [
      history[i - 3].number,
      history[i - 2].number,
      history[i - 1].number,
      history[i].number,
    ].map(Number);

    if ([p1, p2, p3, curr].every((n) => n >= 1 && n <= 49)) {
      matrix[p1][p2][p3][curr]++;
    }
  }

  for (let i = 1; i <= 49; i++) {
    for (let j = 1; j <= 49; j++) {
      for (let k = 1; k <= 49; k++) {
        const row = matrix[i][j][k];
        const sum = row.reduce((a, b) => a + b, 0);
        if (sum > 0) {
          for (let t = 1; t <= 49; t++) {
            row[t] = row[t] / sum;
          }
        }
      }
    }
  }

  return matrix;
}

function getThirdOrderMarkovPrediction(p1, p2, p3, matrix, topN = 5) {
  const row = matrix[p1]?.[p2]?.[p3];
  if (!row) return [];

  return row
    .map((prob, number) => ({ number, prob }))
    .slice(1)
    .filter((item) => item.prob > 0)
    .sort((a, b) => b.prob - a.prob)
    .slice(0, topN)
    .map((item) => item.number);
}

/**
 * 连续段落分析
 */
function analyzeStreaks(arr) {
  let streaks1 = [],
    streaks0 = [];
  let current = arr[0],
    count = 1;

  for (let i = 1; i < arr.length; i++) {
    if (arr[i] === current) count++;
    else {
      (current === 1 ? streaks1 : streaks0).push(count);
      current = arr[i];
      count = 1;
    }
  }

  (current === 1 ? streaks1 : streaks0).push(count);

  const maxStreak1 = Math.max(...streaks1, 0);
  const maxStreak0 = Math.max(...streaks0, 0);

  return {
    maxStreak1,
    maxStreak0,
    avgStreak1: (
      streaks1.reduce((a, b) => a + b, 0) / streaks1.length || 0
    ).toFixed(2),
    avgStreak0: (
      streaks0.reduce((a, b) => a + b, 0) / streaks0.length || 0
    ).toFixed(2),
    streaks1,
    streaks0,
  };
}

// ========== ✅ 主逻辑 ==========
const historyDataRaw = readAllValues("m");
const historyData = historyDataRaw.map((item) => ({
  time: item.time,
  number: Number(item.number),
}));

const windowSize = 100;
const maxRecommendCount = 32;

let correct = 0;
let wrong = 0;
let hitArray = [];

let maxFinalRecommendLen = 32;

for (let i = windowSize; i < historyData.length; i++) {
  const actual = historyData[i];
  const windowData = historyData.slice(i - windowSize, i);

  const p1 = historyData[i - 3]?.number || 0;
  const p2 = historyData[i - 2]?.number || 0;
  const p3 = historyData[i - 1]?.number || 0;

  const prediction = predictNextNumber(windowData);
  const markov1 = getMarkovPrediction(
    p3,
    buildMarkovMatrix(historyData.slice(0, i)),
    5
  );
  const markov2 = getSecondOrderMarkovPrediction(
    p2,
    p3,
    buildSecondOrderMarkovMatrix(historyData.slice(0, i)),
    5
  );
  const markov3 = getThirdOrderMarkovPrediction(
    p1,
    p2,
    p3,
    buildThirdOrderMarkovMatrix(historyData.slice(0, i)),
    5
  );

  // 合并所有候选号码，优先热冷 → 一阶 → 二阶 → 三阶，截断到32个
  const allCandidates = _.uniq([
    ...prediction.recommend,
    ...markov1,
    ...markov2,
    ...markov3,
  ]);
  const finalRecommend = allCandidates
    .slice(0, maxRecommendCount)
    .sort((a, b) => a - b);
  const hit = finalRecommend.includes(actual.number);

  hitArray.push(hit ? 1 : 0);
  hit ? correct++ : wrong++;
  if (finalRecommend.length !== 32) {
    // console.log(`📅 第${i + 1}期（${actual.time}）`);
    // console.log(`🎯 推荐(${finalRecommend.length}):`, finalRecommend.join(", "));
    // console.log(
    //   `✅ 实际号码: ${actual.number} → 是否命中: ${hit ? "✅ 是" : "❌ 否"}`
    // );
    // console.log("---------------------------------------------------");
    if (finalRecommend.length > maxFinalRecommendLen) {
      maxFinalRecommendLen = finalRecommend.length;
      console.log(maxFinalRecommendLen);
    }
  }
}

// ✅ 总结
const { maxStreak1, maxStreak0, avgStreak1, avgStreak0, streaks1, streaks0 } =
  analyzeStreaks(hitArray);
const accuracy = ((correct / (correct + wrong)) * 100).toFixed(2);

console.log("\n🎯 预测结果统计");
console.log(`总预测期数: ${correct + wrong}`);
console.log(`✅ 命中期数: ${correct}`);
console.log(`❌ 错误期数: ${wrong}`);
console.log(`📊 命中率: ${accuracy}%`);

console.log("\n📈 连续预测表现分析：");
console.log(`✅ 最大连续命中次数: ${maxStreak1}`);
console.log(`❌ 最大连续失败次数: ${maxStreak0}`);
console.log(`✅ 成功段落数量: ${streaks1.length}`);
console.log(`❌ 错误段落数量: ${streaks0.length}`);
console.log(`✅ 成功段落平均长度: ${avgStreak1}`);
console.log(`❌ 错误段落平均长度: ${avgStreak0}`);
/*
🎯 预测结果统计
总预测期数: 1145
✅ 命中期数: 751
❌ 错误期数: 394
📊 命中率: 65.59%

📈 连续预测表现分析：
✅ 最大连续命中次数: 13
❌ 最大连续失败次数: 5
✅ 成功段落数量: 252
❌ 错误段落数量: 251
✅ 成功段落平均长度: 2.98
❌ 错误段落平均长度: 1.57
//*/